﻿//-------------------------------------------------------------------
//	Copyright (c) 2010-2014  Zhirnov Andrey
//	This file is part of the "UX Standard Lib" project.
//	See copyright notice in "UX_OCF_Parser.h".
//-------------------------------------------------------------------

#include "CConfig.h"


#ifdef __UX_DEBUG__
#	define CHECK_ITERATOR( _iter )		ASSERT( _iter != null );
#else
#ifdef __UX_FAST__
#	define CHECK_ITERATOR( _iter )
#else
#	define CHECK_ITERATOR( _iter )		if ( _iter != null )
#endif	// __UX_FAST__
#endif	// __UX_DEBUG__


#define CFG_CHECK( _b_check, _text ) \
	if ( !(_b_check) ) {\
		LOG( (string("Config error: ") << _text).cstr(), e_log_msg::ERROR_3 ); \
	}

#define CFG_CHECK_ERR( _b_check, _text ) \
	if ( !(_b_check) ) {\
		LOG( (string("Config error: ") << _text).cstr(), e_log_msg::ERROR_3 ); \
		return false; \
	}



namespace UX_STL
{
namespace UXParser
{

	inline bool CompareObjNames(const uni_c_string_array_t &aLeft, const uni_c_string_array_t &aRight)
	{
		if ( aLeft.Count() != aRight.Count() or aLeft.Back() != aRight.Back() )
			return false;

		const usize	u_count = aLeft.Count()-1;

		for (usize i = 0; i < u_count; ++i) {
			if ( aLeft[i] != aRight[i] )
				return false;
		}
		return true;
	}

	
	inline bool CompareConfigObj(const TConfigObjInfo &sObj1, const CCfgBaseElement *pObj2, bool &bEmptyMatch)
	{
		CCfgObjectElem	*	p_obj2 = (CCfgObjectElem *)pObj2;

		ASSERT( !p_obj2->GetTypeName().Empty() );

		if ( sObj1.bUsed )
			return false;

		// TODO: use 'using'

		// compare type names
		if ( CompareObjNames( sObj1.pIter->GetObjName(), p_obj2->GetFullTypeName() ) )
		{
			if ( sObj1.sName.Empty() and ((CCfgObjectElem *)pObj2)->BindingName().Empty() ) {
				bEmptyMatch = true;
				return false;
			}

			// compare names
			if ( sObj1.sName == ((CCfgObjectElem *)pObj2)->BindingName() )
				return true;
		}
		return false;
	}

	
	class CDummyConfigProcessStrategy : public IConfigProcessStrategy
	{
	public:
		virtual bool Create(static_obj_node_t &pObjects, const CCfgBaseElement *pCfgObj, CMetaSetIterator *&pIter, bool &bNeedDelete)
		{
			usize	u_empty_match = usize(-1);

			FOR( i, pObjects )
			{
				bool	b_is_match = false;	// for empty names

				if ( CompareConfigObj( pObjects[i].Elem(), pCfgObj, b_is_match ) )
				{
					bNeedDelete				 = false;
					pObjects[i].Elem().bUsed = true;
					pIter					 = pObjects[i].Elem().pIter;
					return true;
				}
				if ( b_is_match and u_empty_match == usize(-1) )
				{
					u_empty_match = i;
				}
			}

			if ( u_empty_match != usize(-1) )
			{
				usize	i			  = u_empty_match;
				bNeedDelete			  = false;
				pObjects			  = pObjects[i];
				pObjects.Elem().bUsed = true;
				pIter				  = pObjects.Elem().pIter;
				return true;
			}

			return false;
		}
	};

	static CDummyConfigProcessStrategy	_s_cDummyConfigProcessStrategy;




// --------------------------- CConfig --------------------------- //
/*
=================================================
	конструктор
=================================================
*/
	CConfig::CConfig (): _pOCFParser(null), _bSaveParsed(false)
	{
		_pOCFParser = new OCFParser();
		RegisterParser( _pOCFParser, true );

		_aDefDirectories.PushBack( "" );
	}
	
/*
=================================================
	деструктор
=================================================
*/
	CConfig::~CConfig ()
	{
		//_Clear();
		_pOCFParser->Free();
	}
	
/*
=================================================
	удаляет контейнеры
=================================================
*/
	void CConfig::_Clear ()
	{
		/*while ( !_aContainers.Empty() )
		{
			delete _aContainers.Front();
		}*/
	}

/*
=================================================
	удаляет контэйнер из списка
=================================================
*
	bool CConfig::RemoveContainer (CConfigContainer *pCC)
	{
		FOR( i, _aContainers )
		{
			if ( _aContainers[i] == pCC ) {
				_aContainers.FastErase( i );
				return true;
			}
		}
		return false;
	}
	
/*
=================================================
	добавляет парсер в список
=================================================
*/
	bool CConfig::RegisterParser (IParser *pParser, bool bHighPriority)
	{
		CHECK_ERR( pParser != null );

		UnregisterParser( pParser );

		if ( bHighPriority )	_aParsers.PushFront( pParser );
		else					_aParsers.PushBack( pParser );

		return true;
	}
	
/*
=================================================
	удаляет парсер из списка
=================================================
*/
	bool CConfig::UnregisterParser (IParser *pParser)
	{
		FOR( i, _aParsers )
		{
			if ( pParser == _aParsers[i] ) {
				_aParsers.FastErase( i );
				return true;
			}
		}
		return false;
	}
	
/*
=================================================
	возвращает парсер для файла
=================================================
*/
	bool CConfig::GetParser (uni_c_string sFileExt, IParser *&pParser)
	{
		FOR( i, _aParsers )
		{
			if ( _aParsers[i]->IsSupported( sFileExt ) ) {
				pParser = _aParsers[i];
				return true;
			}
		}
		return false;
	}
	
/*
=================================================
	парсит файл
=================================================
*/
	bool CConfig::Parse (IParser *pParser, RFilePtr pFile, CConfigContainer *&pObj, uint uFileSize, const string_array_t *pDirectories)
	{
		CHECK_ERR( pParser != null );
		pParser->SetDirectories( pDirectories == null ? _aDefDirectories : *pDirectories );

		CConfigContainerPtr	p_config( new CConfigContainer( pFile->Name() ) );

		bool	ret = pParser->ParseFile( pFile, p_config->_sObjectsTree.First(), uFileSize );

		if ( !ret )
		{
			string_array_t const &	a_log = pParser->GetLog();

			LOG( (string("Error while parse file: \"") << pFile->Name() << "\".").cstr(), e_log_msg::ERROR_2 );

			FOR( i, a_log ) {
				LOG( a_log[i].cstr(), e_log_msg::ERROR_NL_2 );
			}
		}

		if ( _bSaveParsed )
		{
			HddWFilePtr		p_file = File::HddWFile::New();

			if ( p_file->Open( string( _sParsedFilesDir ) << '/' << FileAddressUtils::GetNameAndExt( (uni_c_string)pFile->Name() ) << '_' ) )
			{
				pParser->Save( p_file, p_config->_sObjectsTree.First() );
				p_file = null;
			}
		}

		if ( ret )
		{
			pObj = p_config.ptr();
			p_config.Reset();
			//_aContainers.PushBack( pObj );
			return true;
		}

		RETURN_ERR( "can't parse file" );
	}
		
/*
=================================================
	парсит файл
=================================================
*/
	bool CConfig::Parse (RFilePtr pFile, CConfigContainer *&pObj, uint uFileSize, const array<string> *pDirectories)
	{
		CHECK_ERR( pFile.IsNotNull() );
		
		IParser	*p_parser = null;

		CHECK_ERR( GetParser( FileAddressUtils::GetExtension( pFile->Name() ), p_parser ) );
		CHECK_ERR( Parse( p_parser, pFile, pObj, uFileSize, pDirectories ) );
		return true;
	}

/*
=================================================
	парсит файл
=================================================
*/
	bool CConfig::Parse (uni_c_string pFileName, CConfigContainer *&pObj, const array<string> *pDirectories)
	{
		CHECK_ERR( not pFileName.Empty() );

		HddRFilePtr	p_file = File::HddRFile::New();

		CHECK_ERR( p_file->Open( pFileName ) );
		return Parse( p_file, pObj, 0, pDirectories );
	}
	
/*
=================================================
	сохраняет данные в файл
=================================================
*/
	bool CConfig::Save (WFilePtr pFile, save_obj_node_t pObjects)
	{
		CHECK_ERR( pFile.IsNotNull() );

		// TODO ...

		return false;
	}

/*
=================================================
	сохраняет данные в файл
=================================================
*/
	bool CConfig::Save (uni_c_string pFileName, save_obj_node_t pObjects)
	{
		CHECK_ERR( not pFileName.Empty() );

		HddWFilePtr	p_file = File::HddWFile::New();

		CHECK_ERR( p_file->Open( pFileName ) );
		CHECK_ERR( Save( p_file, pObjects ) );
		return true;
	}
	
/*
=================================================
	добавляет стандартную директорию
=================================================
*/
	bool CConfig::AddDirectory (uni_c_string sDirectory)
	{
		ASSERT( not RemoveDirectory( sDirectory ) );
		_aDefDirectories.PushBack( sDirectory );
		return true;
	}
	
/*
=================================================
	удаляет стандартную директорию
=================================================
*/
	bool CConfig::RemoveDirectory (uni_c_string sDirectory)
	{
		return _aDefDirectories.FindAndErase( sDirectory );
	}
	
/*
=================================================
	очищает список директорий
=================================================
*/
	void CConfig::ClearDerectories ()
	{
		_aDefDirectories.Clear();
	}
	
/*
=================================================
	сохранять ли обработанные конфигурационные файлы
	(используется для отладки)
=================================================
*/
	void CConfig::SaveParsedFiles (bool bSave)
	{
		_bSaveParsed = bSave;
	}
	
/*
=================================================
	устанавливает директорию для сохранения
	обработанных конфигурационных файлов
=================================================
*/
	bool CConfig::SetParsedFilesDirectory (uni_c_string pDir)
	{
		CHECK_ERR( FileSystem::IsDirectoryExist( pDir ) );
		
		FileSystem::GetCurrentDirectory( _sParsedFilesDir );
		FileAddressUtils::AddDirectoryToPath( _sParsedFilesDir, pDir );
		return true;
	}

	


// ----------------------- CConfigContainer ---------------------- //
/*
=================================================
	конструктор
=================================================
*/
	CConfigContainer::CConfigContainer(const string &sFileName):
		_sFileName(sFileName)
	{
	}

/*
=================================================
	деструктор
=================================================
*/
	CConfigContainer::~CConfigContainer()
	{
		ClearTree();
		//CONFIG()->RemoveContainer( this );
	}
	
/*
=================================================
	удалить объект
=================================================
*/
	void CConfigContainer::Release()
	{
		delete this;
	}
	
/*
=================================================
	очищает дерево объектов
=================================================
*/
	bool CConfigContainer::ClearTree()
	{
		_sObjectsTree.Clear();
		return true;
	}
	
/*
=================================================
	обработка данных
=================================================
*/
	struct TConfigObjTreeUpdater
	{
		CConfigContainer	*ptr;
		///
		TConfigObjTreeUpdater(CConfigContainer *p): ptr(p) {}

		void operator () (TConfigObjInfo &sInfo)
		{
			ptr->_CheckObj( sInfo );
		}
	};
	
	inline void CConfigContainer::_CheckObj(TConfigObjInfo &sInfo) const
	{
		if ( not sInfo.bUsed )
		{
			RETURN_ERR( "No config objects found, with type \"" << sInfo.pIter->GetObjTypeName() <<
						"\", name \"" << sInfo.sName << "\".", void() );
		}
	}

	bool CConfigContainer::Process(static_obj_node_t pObjects, IConfigProcessStrategy *pStrategy)
	{
		if ( pStrategy == null )
			pStrategy = &_s_cDummyConfigProcessStrategy;

		config_const_node_t	p_node	= _sObjectsTree.First();
		
		_pObjects	= &pObjects;
		_pStrategy	= pStrategy;

		FOR( i, p_node ) {
			CHECK_ERR( _RecursiveSet( p_node[i], null ) );
		}

		_pObjects	= null;
		_pStrategy	= null;

		TConfigObjTreeUpdater	s_upd(this);
		static_obj_tree_t::s_RecursiveUpdateAll( pObjects, s_upd );
		return true;
	}
	
/*
=================================================
	обработка массива
=================================================
*/
	bool CConfigContainer::_RecursiveProcessArray(config_const_node_t pNode, CMetaArrayIterator *pIter)
	{
		if ( !pNode.Valid() or pIter == null or pNode.Empty() )
			return false;

		bool				ret		= true;
		CCfgBaseElement *	p_obj	= pNode[0].Elem();

		if ( p_obj->GetType() == e_config_object::ARRAY or
			 p_obj->GetType() == e_config_object::BINARY_ARRAY or
			 p_obj->GetType() == e_config_object::VARIANT_ARRAY or
			 p_obj->GetType() == e_config_object::OBJECT )
		{
			if ( pIter->GetElemType() == e_value::ARRAY )
			{
				pIter->Resize( pNode.Count() );

				for (usize i = 0; (i < pNode.Count() and ret); ++i)
				{
					CMetaArrayIteratorPtr	p_iter;

					if ( pIter->SelectElem( i ) and pIter->GetArrIterator( p_iter.ref() ) )
					{
						CFG_CHECK_ERR(	_RecursiveProcessArray( pNode[i], p_iter.ptr() ),
										"Can't set value for element number " << string().FormatI(i,10) << '.' );
					}
				}
			}
			else
			if ( pIter->GetElemType() == e_value::OBJECT )
			{
				CMetaSetIteratorPtr	p_iter;

				pIter->Resize( pNode.Count() );
				
				for (usize i = 0; (i < pNode.Count() and ret); ++i)
				{
					if ( !pIter->SelectElem( i )				or
						 !pIter->GetSetIterator( p_iter.ref() )	or
						  p_iter == null )
					{
						ret = false;
						break;
					}
					else
					{
						if ( pNode[i].Elem()->GetType() == e_config_object::OBJECT and
							 !((CCfgObjectElem *)pNode[i].Elem())->IsInitList() )
						{
							config_const_node_t	p_node = pNode[i];
							ret = true;

							// TODO: compare type names

							uni_c_string 	s_field_type_name = ((CMetaObjectType *)p_iter->GetCurObj())->GetObj()->GetFullTypeName().Back();
							uni_c_string 	s_node_type_name  = ((CCfgObjectElem *)p_node.Elem())->GetFullTypeName().Back();

							CFG_CHECK( s_field_type_name == s_node_type_name,
										"Object types mismatch: \"" << s_field_type_name <<
										"\" and \"" << s_node_type_name << '"' );

							FOR( j, p_node ) {
								ret &= _RecursiveSet( p_node[j], p_iter.ptr() );
							}
						}
						else
							ret = _ProcessInitList( pNode[i], p_iter.ptr() );
					}
					CFG_CHECK_ERR( ret, "Can't set value for element number " << string().FormatI(i,10) << '.' );
				}
			}
			else
			if ( pIter->GetElemType() == e_value::BIN_ARRAY )
			{
				pIter->Resize( pNode.Count() );

				for (usize i = 0; (i < pNode.Count() and ret); ++i)
				{
					CMetaStructIteratorPtr	p_iter;

					if ( pIter->SelectElem( i ) and pIter->GetStrIterator( p_iter.ref() ) )
					{
						CFG_CHECK_ERR(	_ProcessStructElem( pNode[i], p_iter.ref() ),
										"Can't set value for element number " << string().FormatI(i,10) << '.' );
					}
				}
			}
		}
		else
		{
			pIter->Resize( pNode.Count() );

			FOR( i, pNode )
			{
				bool	b_res	= false;

				pIter->SelectElem( i );
				p_obj = pNode[i].Elem();

				switch ( p_obj->GetType() )
				{
					case e_config_object::STRING :
						{
							b_res = pIter->SetValue( ((CCfgStringElem *)p_obj)->GetCstr(), e_value::STRING );	
							break;
						}

					case e_config_object::VALUE :
						{
							b_res = pIter->SetValue( ((CCfgNumberElem *)p_obj)->GetValue(), ((CCfgNumberElem *)p_obj)->GetValueType() );
							break;
						}

					default :
						RETURN_ERR( "Unsupported config object type." );
				}

				CFG_CHECK_ERR( b_res, "Can't set value for element number " << string().FormatI(i,10) << '.' );
			}

			ret = true;
		}

		return ret;
	}
/*
=================================================
	обработка массива
=================================================
*/
	bool CConfigContainer::_RecursiveProcessVarArray(config_const_node_t pNode, CMetaVarArrayIterator *pIter)
	{
		if ( !pNode.Valid() or pIter == null or pNode.Empty() )
			return false;
			
		CCfgBaseElement *	p_obj	= null;

		pIter->Resize( pNode.Count() );

		FOR( i, pNode )
		{
			bool	b_res = false;
			p_obj = pNode[i].Elem();

			CHECK_ERR( pIter->SelectElem( i ) );

			switch ( p_obj->GetType() )
			{
				case e_config_object::OBJECT :
					{
						CMetaSetIteratorPtr	p_iter;
						config_const_node_t	p_node = pNode[i];
						const uni_c_string	s_node_type_name  = ((CCfgObjectElem *)p_node.Elem())->GetFullTypeName().Back();

						
						CHECK_ERR( pIter->GetSetIterator( s_node_type_name, p_iter.ref() ) );
						
						b_res = true;

						FOR( j, p_node ) {
							b_res &= _RecursiveSet( p_node[j], p_iter.ptr() );
						}

						CHECK_ERR( b_res, "error while process object." );
						break;
					}

				case e_config_object::ARRAY :
					{
						CMetaArrayIteratorPtr	p_arr_iter;
						const uni_c_string 		s_type_name = ((CCfgArrayElem *)pNode[i].Elem())->GetTypeName();
					
						CHECK_ERR( pIter->GetArrIterator( s_type_name, p_arr_iter.ref() ) );
						CHECK_ERR( _RecursiveProcessArray( pNode[i], p_arr_iter.ptr() ) );
						b_res = true;
						break;
					}

				case e_config_object::VARIANT_ARRAY :
					{
						CMetaVarArrayIteratorPtr	p_arr_iter;
					
						CHECK_ERR( pIter->GetVarIterator( p_arr_iter.ref() ) );
						CHECK_ERR( _RecursiveProcessVarArray( pNode[i], p_arr_iter.ptr() ) );
						b_res = true;
						break;
					}

				case e_config_object::BINARY_ARRAY :
					{
						CMetaStructIteratorPtr	p_st_iter;
					
						CHECK_ERR( pIter->GetStrIterator( p_st_iter.ref() ) );
						CHECK_ERR( _ProcessStructElem( pNode[i], p_st_iter.ptr() ) );
						b_res = true;
						break;
					}

				case e_config_object::STRING :
					{
						b_res = pIter->SetValue( ((CCfgStringElem *)p_obj)->GetCstr(), e_value::STRING );
						break;
					}

				case e_config_object::VALUE :
					{
						b_res = pIter->SetValue( ((CCfgNumberElem *)p_obj)->GetValue(), ((CCfgNumberElem *)p_obj)->GetValueType() );
						break;
					}

				default :
					RETURN_ERR( "Unsupported config object type." );
			};
			
			CFG_CHECK_ERR( b_res, "Can't set value for element number " << string().FormatI(i,10) << '.' );
		}
		return true;
	}
	
/*
=================================================
	обработка структуры (бинарного массива)
=================================================
*/
	bool CConfigContainer::_ProcessStructElem(config_const_node_t pNode, CMetaStructIterator *pIter)
	{
		if ( !pNode.Valid() or pIter == null )
			return false;

		CCfgBaseElement		*	p_obj	= null;
		e_config_object::type	e_type	= e_config_object::UNKNOWN;

		FOR( i, pNode )
		{
			bool	b_res = false;

			p_obj = pNode[i].Elem();

			switch ( p_obj->GetType() )
			{
				case e_config_object::VALUE :
					{
						b_res = pIter->PushBackValue( ((CCfgNumberElem *)p_obj)->GetValue(), ((CCfgNumberElem *)p_obj)->GetValueType() );
						break;
					}

				default :
					RETURN_ERR( "Unsupported config object type." );
			}

			CFG_CHECK_ERR( b_res, "Can't set value for element number " << string().FormatI(i,10) << '.' );
		}

		return true;
	}

/*
=================================================
	обработка списка инициализации
=================================================
*/
	bool CConfigContainer::_ProcessInitList(config_const_node_t pNode, CMetaSetIterator *pIter)
	{
		if ( !pNode.Valid() or pIter == null )
			return false;

		CCfgBaseElement	 *	p_obj	= null;

		FOR( i, pNode )
		{
			bool	b_res	= false;
			p_obj = pNode[i].Elem();

			switch ( p_obj->GetType() )
			{
				case e_config_object::STRING :
					{
						b_res = pIter->SetValue( i, ((CCfgStringElem *)p_obj)->GetCstr(), e_value::STRING );	
						break;
					}

				case e_config_object::VALUE :
					{
						b_res = pIter->SetValue( i, ((CCfgNumberElem *)p_obj)->GetValue(), ((CCfgNumberElem *)p_obj)->GetValueType() );
						break;
					}

				default :
					RETURN_ERR( "Unsupported config object type." );
			}

			CFG_CHECK_ERR( b_res, "Can't set value for element number " << string().FormatI(i,10) << '.' );
		}

		return true;
	}
	
/*
=================================================
	обработка статичных объектов
=================================================
*/
	bool CConfigContainer::_RecursiveSet(config_const_node_t pNode, CMetaSetIterator *pIter)
	{
		CCfgBaseElement	*	p_obj		= pNode.Elem();
		bool				b_new_iter	= false;

		CHECK_ERR( p_obj != null );

		switch ( p_obj->GetType() )
		{
			case e_config_object::CONSTANT :
			case e_config_object::ENUM :
			case e_config_object::NAMESPACE :
			case e_config_object::USING :
			case e_config_object::SCRIPT :
				return true;

			case e_config_object::OBJECT :
				{
					// field
					if ( !p_obj->GetFieldName().Empty() )
					{
					CHECK_ITERATOR( pIter )
					{
						pIter = new CMetaSetIterator( *pIter, true );
							
						if ( pIter->FindObject( p_obj->GetFieldName() ) )
						{
							if ( pIter->GetCurObj()->GetType() != e_value::OBJECT ) {
								pIter->Free();
								RETURN_ERR( "Field \"" << p_obj->GetFieldName() << "\" is not object type" );
							}

							if ( ((CCfgObjectElem *)p_obj)->IsInitList() )
							{
								bool	ret = _ProcessInitList( pNode, pIter );
								pIter->Free();

								CHECK_ERR( ret, "error while process init list" );
								return true;
							}
							else
							{
								// TODO: compare type names

								uni_c_string 	s_field_type_name = ((CMetaObjectType *)pIter->GetCurObj())->GetObj()->GetFullTypeName().Back();
								uni_c_string 	s_node_type_name  = ((CCfgObjectElem *)pNode.Elem())->GetFullTypeName().Back();

								CFG_CHECK( s_field_type_name == s_node_type_name,
											"Object types mismatch: \"" << s_field_type_name <<
											"\" and \"" << s_node_type_name << '"' );

								if ( pNode.Empty() ) {
									pIter->Free();
									return true;
								}

								b_new_iter = true;
								break;
							}
						}
						else
						{
							pIter->Free();
							RETURN_ERR( "can't find object \"" << p_obj->GetFieldName() << '"' );
						}
					}}
					else
					{
						b_new_iter = false;

						CFG_CHECK_ERR(
							_pStrategy->Create( *_pObjects, p_obj, pIter, b_new_iter ) and pIter != null,
							"No object found, with type name: \"" << ((CCfgObjectElem *)p_obj)->GetTypeName() <<
							"\", binding name: \"" << ((CCfgObjectElem *)p_obj)->BindingName() << "\", in namespace: \"" <<
							_pObjects->Elem().sName << "\""
						);
					
						if ( ((CCfgObjectElem *)p_obj)->IsInitList() )
						{
							bool	b_res = _ProcessInitList( pNode, pIter );
							pIter->Free();

							CHECK_ERR( b_res, "error while process init list" );
							return true;
						}
					}
					break;
				}

			case e_config_object::ARRAY :
				CHECK_ITERATOR( pIter )
				{
					CMetaArrayIteratorPtr	p_arr_iter;

					CHECK_ERR( pIter->GetArrayIterator( p_obj->GetFieldName(), p_arr_iter.ref() ) );
					CHECK_ERR( _RecursiveProcessArray( pNode, p_arr_iter.ptr() ) );
					return true;
				}

			case e_config_object::VARIANT_ARRAY :
				CHECK_ITERATOR( pIter )
				{
					CMetaVarArrayIteratorPtr	p_arr_iter;

					CHECK_ERR( pIter->GetVarArrayIterator( p_obj->GetFieldName(), p_arr_iter.ref() ) );
					CHECK_ERR( _RecursiveProcessVarArray( pNode, p_arr_iter.ptr() ) );
					return true;
				}

			case e_config_object::BINARY_ARRAY :
				CHECK_ITERATOR( pIter )
				{
					CMetaStructIteratorPtr	p_st_iter;

					CHECK_ERR( pIter->GetStructIterator( p_obj->GetFieldName(), p_st_iter.ref() ) );
					CHECK_ERR( _ProcessStructElem( pNode, p_st_iter.ptr() ) );
					return true;
				}

			case e_config_object::STRING :
				CHECK_ITERATOR( pIter )
				{
					CFG_CHECK( pIter->SetValue( p_obj->GetFieldName(), ((CCfgStringElem *)p_obj)->GetCstr(), e_value::STRING ),
								"Can't set value with name \"" << p_obj->GetFieldName() << '"' );
					break;
				}

			case e_config_object::VALUE :
				CHECK_ITERATOR( pIter )
				{
					CFG_CHECK( pIter->SetValue( p_obj->GetFieldName(), ((CCfgNumberElem *)p_obj)->GetValue(), ((CCfgNumberElem *)p_obj)->GetValueType() ),
								"Can't set value with name \"" << p_obj->GetFieldName() << '"' );
					break;
				}

			default :
				RETURN_ERR( "Unsupported config object type." );
		}

		bool	b_res = true;

		for (usize i = 0; (i < pNode.Count() and b_res); ++i)
		{
			b_res = _RecursiveSet( pNode[i], pIter );
		}

		if ( b_new_iter and pIter != null ) {
			pIter->Free();
		}
		
		CHECK_ERR( b_res, "error in recursive set" );
		return true;
	}

//-------------------------------------------------------------------

}	// UXParser
}	// UX_STL